
#include "sdk_common.h"
#include "sdk_proto.h"
#include "sdk_utils.h"
#include "sdk_port.h"
#include <stdio.h>
#include <string.h>
#include "netinet/in.h"
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/fcntl.h>
#include "sdk.h"
#include "cJSON.h"


#define SDK_AUTH_PORT   9000
#define SDK_DATA_PORT   9001

Fp_HeartBeat_Cb func_ptr = NULL;
uint8_t * auth_buffer=NULL;
int auth_buffer_len = -1;
uint8_t * data_buffer=NULL;
int data_buffer_len = -1;
static sdk_port_thread_t auth_thread = -1;
static sdk_port_thread_t data_thread = -1;
int sdk_socket_auth_fd = -1;
int sdk_socket_data_fd = -1;
O_SDK_CONTEXT sdk_context;
uint8_t sndbuf[SOCKET_SEND_BUFFER_LENGTH_MAX]={0};
uint8_t sdktid=0;
uint8_t gretry = 0;
struct sockaddr_in auth_sock_addr_in;
socklen_t auth_sock_addr_len = sizeof(auth_sock_addr_in);
struct sockaddr_in data_sock_addr_in;
socklen_t data_sock_addr_len = sizeof(data_sock_addr_in);

pFuncDeviceOnoffline_t device_onoffline_cb = NULL;
pFuncDeviceExtdata_t device_extdata_cb = NULL;
pFuncDevicedata_t device_data_cb = NULL;

static uint16_t  ya_3rd_auth_payload_buffer_fill(uint8_t * buf,uint16_t len)
{
    if(buf==NULL){
        //ya_printf(C_LOG_ERROR,"%s:null pointer.\r\n",__FUNCTION__);
        sdk_printf("%s:null pointer.",__FUNCTION__);
        return 0;
    }
    if(len > 1000 ){
        //ya_printf(C_LOG_ERROR,"%s:buffer len > 1000 bytes.\r\n",__FUNCTION__);
        sdk_printf("%s:buffer len > 1000 bytes.",__FUNCTION__);
        return 0;
    }

    memset(sndbuf,0,sizeof(sndbuf));
    sndbuf[0]=0xf9;
    sndbuf[1]=0x09;
    //type
    sndbuf[2]=0x00;

    //reserved
    sndbuf[5]=0x00;
    //random
    int32_t random = sdk_port_random_get();
    ya_3rd_pkg_random_lake_put(random & 0xFFFF);
    sndbuf[6]= random & 0xFF;
    sndbuf[7]= (random>>8)&0xFF;
    //payload
    memcpy(sndbuf+8,buf,len);
    //pkg len
    sndbuf[3]=(len+8+2)&0xFF;
    sndbuf[4]=((len+8+2)>>8)&0xFF;
    return (len+8+2);//crc16 padding
}




static int ya_3rd_intf_package_encrypt(unsigned char *data,unsigned short len)
{
    uint8_t factor0 = ~(sndbuf[6]);
    uint8_t factor1 = ~(sndbuf[7]);
    sdk_printf("factor0[%02x],factor1[%02x]\r\n",factor0,factor1);
    //int16_t random = *((int16_t *)(sndbuf+6));
    int i = 0;

    if(data == NULL || len < 10){
        sdk_printf("%s: param error.\r\n",__FUNCTION__);
        return SDK_PARAMS_ERR;
    }

    for(;i<(len-2);i++){
        if(i==6 || i==7)
            continue;
        
        if((i&0x01)){
            sndbuf[i] ^=factor1;
        }else{
            sndbuf[i] ^=factor0;
        }
        //sdk_printf("%02x ",sndbuf[i]);
    }
    
    //crc16
    uint16_t crc16_ccit = CRC16_CCITT_calc(sndbuf,len-2,0);
    sndbuf[len-2] = (crc16_ccit & 0xFF) ^factor0;
    sndbuf[len-1] = ((crc16_ccit>>8) & 0xFF) ^factor1;
    sdk_printf("%02x %02x\r\n",sndbuf[len-2],sndbuf[len-1]);
    //bits shift right
    for(i=0;i<len;i++){
        sndbuf[i] = ROTATE_RIGHT(sndbuf[i],8,i%8);
    }
    //sdk_printf("\r\n");
    return SDK_OK;
}

/*
接口申请包，相隔1s x (N+1)才能发送，N位失败次数
[f9][09][00][len0][len1][00][randon0][randon1][payload(unencrypted)][pkgchksum]
*/
static int ya_3rd_intf_package_decrypt(P_3RD_PAYLOAD p,unsigned char *data,unsigned short len)
{
    //bits shift left
    int i =0;
    for(i=0;i<len;i++){
        data[i] = ROTATE_LEFT(data[i],8,i%8);
    }
    /*
    sdk_printf("%s\r\n",__FUNCTION__);
    for(i=0;i<len;i++){
        sdk_printf("%02x ",data[i]);
    }
    sdk_printf("\r\n");
    */

    //random
    uint8_t factor0 = ~(data[6]);
    uint8_t factor1 = ~(data[7]);
    //sdk_printf("factor0[%02x],factor1[%02x]\r\n",factor0,factor1);

    //crc16
    uint16_t crc16_ccit_calc = CRC16_CCITT_calc(data,len-2,0);
    uint16_t crc16_ccit= (data[len-2] & 0xFF) ^factor0;
    crc16_ccit |= ((data[len-1] & 0xFF) ^factor1)<<8;
    if(crc16_ccit != crc16_ccit_calc){
        sdk_printf("%s: crc16_ccit_calc[%04x],crc16_ccit[%04x] checksum error.\r\n",__FUNCTION__,crc16_ccit_calc,crc16_ccit);
        return -1;
    }

    for(i=0;i<(len-2);i++){
        if(i==6 || i==7)
            continue;
        
        if((i&0x01)){
            data[i] ^=factor1;
        }else{
            data[i] ^=factor0;
        }
        //sdk_printf("%02x ",sndbuf[i]);
    }
    //sdk_printf("\r\n");
    //check data
    if(data[0]!=0xF9 || data[1]!=0x09){
        sdk_printf("%s:decrypt error.\r\n",__FUNCTION__);
        return -2;
    }

    //check length
    uint16_t pkg_len = data[3] |(data[4]<<8);
    if(len!=pkg_len){
        sdk_printf("%s:package length error.recvlen[%u],parselen[%u]\r\n",__FUNCTION__,len,pkg_len);
        return -3;
    }

    //
    p->payload_len = pkg_len-8-2;
    p->random[0] = ~(factor0);
    p->random[1] = ~(factor1);
    memcpy(p->chksum,&crc16_ccit,2);
    p->p_payload = data+8;
    if(ya_3rd_pkg_random_lake_check(p->random[0] | (p->random[1]<<8))){
        sdk_printf("%s: random repeat.random0[%02x],random1[%02x]\r\n",__FUNCTION__,p->random[0],p->random[1]);
        return -4;
    }
    //sdk_printf("%s:random0[%02x],random1[%02x]\r\n",__FUNCTION__,p->random[0],p->random[1]);
    ya_3rd_pkg_random_lake_put( p->random[0] | (p->random[1]<<8));
    return 0;
}




/*
数据通讯包，建议至少100ms间隔，可多次发送相同的包，确保对方收到
[f9][09][01][len0][len1][00][randon0][randon1][payload(encrypted)][pkgchksum]
*/
int ya_3rd_intf_payload_encrypt(unsigned char *data,unsigned short len)
{
    int ret = -1;
    int totol = strlen(sdk_context.accesskey);
    if(totol==0){
        //ya_printf(C_LOG_ERROR,"%s: auth context is null.\r\n",__FUNCTION__);
        sdk_printf("%s: auth context is null.",__FUNCTION__);
        sdk_printf("no thrid part device connect.");
        return ret;
    }

    int i = 0,j=0;
    for(;i<len;i++,j++){
        if(j>totol || j==totol){
            j=0;
        }

       data[i] ^= sdk_context.accesskey[j];
    }
    return 0;
}






/*
数据通讯包，建议至少100ms间隔，可多次发送相同的包，确保对方收到
[f9][09][01][len0][len1][00][randon0][randon1][payload(encrypted)][pkgchksum]
*/
int ya_3rd_intf_payload_decrypt(P_3RD_PAYLOAD p,unsigned char *data,unsigned short len)
{
    int ret = -1;
    int totol = strlen(sdk_context.accesskey);
    if(totol==0){
        //ya_printf(C_LOG_ERROR,"%s: auth context is null.\r\n",__FUNCTION__);
        sdk_printf("%s: auth context is null.\r\n",__FUNCTION__);
        return ret;
    }

    //calc msg length
    int msglen = len - 10 > 0?len - 10:-1;
    if(msglen < 0){
        //ya_printf(C_LOG_ERROR,"%s: package error.\r\n",__FUNCTION__);
        sdk_printf("%s: package error.\r\n",__FUNCTION__);
        return ret;       
    }


    int i = 0,j=0;
    for(;i<p->payload_len;i++,j++){
        if(j>totol || j==totol){
            j=0;
        }

       p->p_payload[i] ^= sdk_context.accesskey[j];
    }

    return 0;
}



int sdk_proto_data_send(int sockfd,uint8_t *data,uint16_t len,uint8_t retry)
{
	uint16_t pkg_len = ya_3rd_auth_payload_buffer_fill(data,len);
    sdk_printf("pkg_len=%d\r\n",pkg_len);
    
    int sndnum = 0;
    if(ya_3rd_intf_package_encrypt(sndbuf,pkg_len) == SDK_OK){
        for(uint8_t j=0;j<retry;j++){
            if(sdk_socket_auth_fd==sockfd){
                sndnum = sdk_port_socket_sendto(sdk_socket_auth_fd, sndbuf,pkg_len, 0, (struct sockaddr*)&auth_sock_addr_in, auth_sock_addr_len);
                sdk_printf("%s:auth send[%d]\r\n",__FUNCTION__,sndnum);
            }else if(sdk_socket_data_fd==sockfd){
                sndnum = sdk_port_socket_sendto(sdk_socket_data_fd, sndbuf,pkg_len, 0, (struct sockaddr*)&data_sock_addr_in, data_sock_addr_len);
                sdk_printf("%s:data send[%d]\r\n",__FUNCTION__,sndnum);
            }else{
                sdk_printf("%s:sdk socket fd error.\r\n",__FUNCTION__);
                return SDK_SOCKSEND_ERR;
            }                              
        }
    }
    return sndnum;
}








P_SDK_CONTEXT sdk_proto_context_get(void)
{
    return &sdk_context;
}


void sdk_proto_context_clear(void)
{
    memset(&sdk_context,0,sizeof(sdk_context));
}

void sdk_proto_context_unregist_clear(void)
{
    memset(&sdk_context.accesskey,0,sizeof(sdk_context.accesskey));
    memset(&sdk_context.decsUrl,0,sizeof(sdk_context.decsUrl));
    memset(&sdk_context.authOrig,0,sizeof(sdk_context.authOrig));
}


int sdk_proto_auth_socket_init(void)
{   
    return sdk_port_udp_socket_create(&sdk_socket_auth_fd,sdk_context.auth_port);
}

uint8_t sdk_proto_get_tid(void)
{
    return sdktid++;
}

uint8_t sdk_proto_send_retry_set(uint8_t retry)
{
    if(retry ==0){
        gretry = 2;
    }else if(retry < 10){
        gretry = retry;
    }else{
        gretry =10;
    }
    return gretry;
}
uint8_t sdk_proto_send_retry_get(void)
{
    return gretry;
}


void sdk_proto_license_load(uint8_t devid[35],uint8_t productKey[15],uint8_t deviceSecret[35])
{
    memcpy(sdk_context.devid,devid,sizeof(sdk_context.devid));
    memcpy(sdk_context.productKey,productKey,sizeof(sdk_context.productKey));
    memcpy(sdk_context.deviceSecret,deviceSecret,sizeof(sdk_context.deviceSecret));
}


int sdk_proto_target_set(char *ip)
{
    if(ip==NULL){
        sdk_printf("%s:null pointer.\r\n",__FUNCTION__);
        return SDK_PARAMS_ERR;
    }
    memcpy(sdk_context.targetIp,ip,strlen(ip));
    return SDK_OK;
}


int sdk_proto_target_set_authport(unsigned short port)
{
    if(port){
        sdk_context.auth_port = port;
    }else{
        sdk_context.auth_port = SDK_AUTH_PORT;
    }
    
    return SDK_OK;
}

int sdk_proto_target_set_dataport(unsigned short port)
{
    if(port){
        sdk_context.port = port;
    }else{
        sdk_context.port = SDK_DATA_PORT;        
    }
    
    return SDK_OK;
}




int sdk_proto_auth(uint8_t retry)
{
    char json[500]={0};
    uint8_t mac[13]={0};
    char macstring[20]={0};
    char ipstring[20]={0};
    sdk_port_get_mac_addr(mac);
    sdk_utils_hexArray2string(mac,6,macstring);
    sdk_port_get_ip(ipstring);
    sprintf(json,"{\"call\":\"ai.argrace.gw.auth\",\"ak\":\"%s\",\"pk\":\"%s\",\"id\":\"%s\",\"sc\":\"%s\",\"mac\":\"%s\",\"ip\":\"%s\",\"authport\":\"%u\",\"dataport\":\"%u\"}",\
        sdk_context.accesskey,sdk_context.productKey,sdk_context.devid,sdk_context.deviceSecret,macstring,ipstring,sdk_context.auth_port,sdk_context.port);
    sdk_printf("%s:%s\r\n",__FUNCTION__,json);
    return sdk_proto_auth_send((uint8_t *)json,strlen(json),retry);
}

int sdk_proto_unregist(uint8_t retry)
{
    char json[500]={0};
    uint8_t mac[13]={0};
    char macstring[20]={0};
    char ipstring[20]={0};
    sdk_port_get_mac_addr(mac);
    sdk_utils_hexArray2string(mac,6,macstring);
    sdk_port_get_ip(ipstring);
    sprintf(json,"{\"call\":\"ai.argrace.gw.unregist\",\"ak\":\"%s\",\"pk\":\"%s\",\"id\":\"%s\",\"sc\":\"%s\",\"mac\":\"%s\",\"ip\":\"%s\"}",\
        sdk_context.accesskey,sdk_context.productKey,sdk_context.devid,sdk_context.deviceSecret,macstring,ipstring);
    sdk_printf("%s:%s\r\n",__FUNCTION__,json);
    return sdk_proto_auth_send((uint8_t *)json,strlen(json),retry);
}



int sdk_proto_get_descfile(char *buffer,int len)
{
    if(strlen(sdk_context.accesskey)==0){
        sdk_printf("%s:unauth error.\r\n",__FUNCTION__);
        return SDK_UNAUTH_ERR;
    }
    char url[512]={0};
    sprintf(url,"http://%s/desc/%s.json",sdk_context.targetIp,sdk_context.devid);
    if(strlen(url)>len){
        sdk_printf("%s:buffer too short!\r\n",__FUNCTION__);
        return SDK_BUFFER_ERR;
    }
    strcpy(buffer,url);
    if(!strstr(buffer,".json")){
        sdk_printf("%s:buffer operation error.\r\n",__FUNCTION__);
        return SDK_BUFFER_ERR;
    }
    return SDK_OK;
}



int sdk_proto_auth_recv(uint8_t *buf,long unsigned int len)
{
    if(buf==NULL || len ==0){
        sdk_printf("%s:null pointer.\r\n",__FUNCTION__);
        return SDK_NULLPTR_ERR;
    }
    uint8_t buffer[512]={0};
    //int sdk_port_socket_recvform(int fd, const void *buf, long unsigned int len, int flags, struct sockaddr *source_addr, unsigned int *source_len)
    int pkglen = sdk_port_socket_recvform(sdk_socket_auth_fd,buffer,sizeof(buffer),0,(struct sockaddr *)&auth_sock_addr_in,&auth_sock_addr_len);
    if(pkglen<0 || pkglen==0){
        sdk_printf("%s: recv error\r\n",__FUNCTION__);
        return SDK_SOCKRECV_ERR;
    }

    O_3RD_PAYLOAD pl;
    memset(&pl,0,sizeof(pl));
    if(ya_3rd_intf_package_decrypt(&pl,buffer,pkglen)<0){
        sdk_printf("%s:package decrypt error\r\n",__FUNCTION__);
        return SDK_DECRYPT_ERR;
    }

    uint32_t jsonlen = pl.payload_len > len? len-1:pl.payload_len;
    memcpy(buf,pl.p_payload,jsonlen);
    sdk_printf("%s:jsonlen[%u],%s\r\n",__FUNCTION__,jsonlen,(char *)buf);

/*
    //context record
    cJSON *root = cJSON_Parse((const char *)buf);
    if(!root){
        sdk_printf("%s: auth json parse error.\r\n",__FUNCTION__);
        return SDK_PARSE_ERR;
    }

    cJSON *jak = cJSON_GetObjectItem(root,"ak");
    cJSON *jdid = cJSON_GetObjectItem(root,"id");
    cJSON *jcode = cJSON_GetObjectItem(root,"code");
    cJSON *jgwid = cJSON_GetObjectItem(root,"gwid");
    cJSON *jport = cJSON_GetObjectItem(root,"port");
    cJSON *jmac = cJSON_GetObjectItem(root,"mac");
    cJSON *jip = cJSON_GetObjectItem(root,"ip");
    if(jak==NULL || jdid==NULL || jcode==NULL || jport==NULL || jgwid==NULL || jip==NULL ||jmac==NULL){
        sdk_printf("%s: auth json key not found.\r\n",__FUNCTION__);
        return SDK_PARSE_ERR;
    }    

    memset(sdk_context.accesskey,0,sizeof(sdk_context.accesskey));
    strcpy(sdk_context.accesskey,jak->valuestring);
    memset(sdk_context.authOrig,0,sizeof(sdk_context.authOrig));
    strcpy(sdk_context.authOrig,(const char *)buf);
    memset(sdk_context.mac,0,sizeof(sdk_context.mac));
    strcpy(sdk_context.mac,jmac->valuestring);
    memset(sdk_context.ip,0,sizeof(sdk_context.ip));
    strcpy(sdk_context.ip,jip->valuestring);
    sdk_context.port = atoi(jport->valuestring) &0xFFFF;
    sdk_context.gwid = atoi(jgwid->valuestring) &0xFFFF;
*/
    return jsonlen;
}

int sdk_proto_data_recv(uint8_t *buf,long unsigned int len)
{
    if(buf==NULL || len ==0){
        sdk_printf("%s:null pointer.\r\n",__FUNCTION__);
        return SDK_NULLPTR_ERR;
    }
    uint8_t buffer[1024]={0};
    //int sdk_port_socket_recvform(int fd, const void *buf, long unsigned int len, int flags, struct sockaddr *source_addr, unsigned int *source_len)
    int pkglen = sdk_port_socket_recvform(sdk_socket_data_fd,buffer,sizeof(buffer),0,(struct sockaddr *)&data_sock_addr_in,&data_sock_addr_len);
    if(pkglen<0 || pkglen==0){
        sdk_printf("%s: recv error\r\n",__FUNCTION__);
        return SDK_SOCKRECV_ERR;
    }
    O_3RD_PAYLOAD pl;
    memset(&pl,0,sizeof(pl));
    if(ya_3rd_intf_package_decrypt(&pl,buffer,pkglen)<0){
        sdk_printf("%s:package decrypt error\r\n",__FUNCTION__);
        return SDK_DECRYPT_ERR;
    }

    if(ya_3rd_intf_payload_decrypt(&pl,buffer,pkglen)!=0){
        sdk_printf("%s:payload decrypt error\r\n",__FUNCTION__);
        return SDK_DECRYPT_ERR;
    }
    uint32_t jsonlen = pl.payload_len > len? len-1:pl.payload_len;
    memcpy(buf,pl.p_payload,jsonlen);
    sdk_printf("%s:jsonlen[%u],%s\r\n",__FUNCTION__,jsonlen,(char *)buf);

    return jsonlen;
}




int sdk_proto_ctl_send(uint8_t *pdata,uint16_t len)
{
    if(pdata==NULL || len ==0){
        sdk_printf("%s:null pointer.\r\n",__FUNCTION__);
        return SDK_NULLPTR_ERR;
    }

    if(strlen(sdk_context.accesskey)==0){
        sdk_printf("%s:unauth error.\r\n",__FUNCTION__);
        return SDK_UNAUTH_ERR;
    }

    if(ya_3rd_intf_payload_encrypt(pdata,len)){
        sdk_printf("%s:payload encrypt error.\r\n",__FUNCTION__);
        return SDK_ENCRYPT_ERR;
    }

    uint32_t ipaddr = sdk_port_inet_addr(sdk_context.targetIp);
	memset(&data_sock_addr_in, 0, sizeof(data_sock_addr_in));
	data_sock_addr_in.sin_family = AF_INET;
	data_sock_addr_in.sin_addr.s_addr = ipaddr;
	data_sock_addr_in.sin_port = sdk_port_htons(sdk_context.port);
    return sdk_proto_data_send(sdk_socket_data_fd,pdata,len,sdk_proto_send_retry_get());
}

int sdk_proto_auth_send(uint8_t *pdata,uint16_t len,uint8_t retry)
{
    if(pdata==NULL || len ==0){
        sdk_printf("%s:null pointer.\r\n",__FUNCTION__);
        return SDK_NULLPTR_ERR;
    }
    uint32_t ipaddr = sdk_port_inet_addr(sdk_context.targetIp);

	memset(&auth_sock_addr_in, 0, sizeof(auth_sock_addr_in));
	auth_sock_addr_in.sin_family = AF_INET;
	auth_sock_addr_in.sin_addr.s_addr = ipaddr;
	auth_sock_addr_in.sin_port = sdk_port_htons(SDK_AUTH_PORT);

     
    return sdk_proto_data_send(sdk_socket_auth_fd,pdata,len,retry);
}


int sdk_proto_cloud_report(uint16_t addr, uint8_t * msg,uint16_t msglen)
{
    char json[1024]={0};
    if(msglen > (sizeof(json)-1)){
        sdk_printf("%s: msg too long.\r\n",__FUNCTION__);
        return SDK_BUFFER_ERR;
    }

    sprintf(json,"{\"call\":\"ai.argrace.gw.cloud.report\",\"ak\":\"%s\",\"ad\":\"%04x\",\"extdata\":\"%s\"}",\
        sdk_context.accesskey,addr,msg);
    sdk_printf("%s:%s\r\n",__FUNCTION__,json);

    
    return sdk_proto_ctl_send((uint8_t*)json,strlen(json));
}



int sdk_proto_hello_send(Fp_HeartBeat_Cb cb,int code)
{
    if(cb==NULL){
        sdk_printf("%s:null pointer\r\n",__FUNCTION__);
        return SDK_PARAMS_ERR;
    }
    func_ptr  = cb;
    char json[1024]={0};
    sprintf(json,"{\"call\":\"ai.argrace.gw.hello\",\"ak\":\"%s\",\"ad\":\"%04x\",\"extdata\":\"%s\",\"code\":%d,\"type\":\"%s\",\"did\":\"%s\"}",sdk_context.accesskey,0,"",code,"device",sdk_context.devid);
    sdk_printf("%s:%s\r\n",__FUNCTION__,json);

    return sdk_proto_auth_send((uint8_t *)json,strlen(json),1);
}



void sdk_proto_device_onoffline_funcb_regist(pFuncDeviceOnoffline_t pfunc)
{
    if(pfunc){
        device_onoffline_cb = pfunc;
    }
}
void sdk_proto_device_extdata_funcb_regist(pFuncDeviceExtdata_t pfunc)
{
    if(pfunc){
        device_extdata_cb = pfunc;
    }
}
void sdk_proto_device_data_funcb_regist(pFuncDevicedata_t pfunc)
{
    if(pfunc){
        device_data_cb = pfunc;
    }
}



void *sdk_proto_data_recv_thread(void *arg)
{
    O_DEVICE_DATA data;
    uint8_t data_cache[100]={0};
    while(1){
        memset(data_buffer,0,data_buffer_len);
        int ret = sdk_proto_data_recv(data_buffer,data_buffer_len);
        if(ret<0){
            sdk_printf("%s: recv error\r\n",__FUNCTION__);
            continue;
        }

        cJSON *root = cJSON_Parse((const char *)data_buffer);
        if(!root){
            sdk_printf("%s: data json pare error\r\n",__FUNCTION__);
            continue;
        }

        //handle data      
        uint16_t addr=0;
        cJSON *jaddr = cJSON_GetObjectItem(root,"ad");
        if(jaddr==NULL){
            sdk_printf("%s: ad key not found.\r\n",__FUNCTION__);
            continue;
        }
        sdk_utils_string2hexArray((uint8_t*)&addr,2,jaddr->valuestring);



        if(strstr((const char *)data_buffer,"ai.argrace.gw.cloud.set")){
            //{"call":"ai.argrace.gw.cloud.set","ak":"282247500b7d405786ecf3e8bc53087216388844a9a44c3fa4460b78ca8b7bdd","ad":"1190","extdata":"12dbac357abdf33"}
            cJSON *jak= cJSON_GetObjectItem(root,"ak");
            if(jak==NULL){
                sdk_printf("%s: ak key not found.\r\n",__FUNCTION__);
                continue;
            }
            if(strcmp((const char *)sdk_context.devid,jak->valuestring)){
                sdk_printf("%s: accesskey is not match.\r\n",__FUNCTION__);
                continue;
            }


            cJSON *jextdata = cJSON_GetObjectItem(root,"extdata");
            if(jextdata==NULL){
                sdk_printf("%s: extdata key not found.\r\n",__FUNCTION__);
                continue;
            }
            if(device_extdata_cb){
                device_extdata_cb(addr,jextdata->valuestring,strlen(jextdata->valuestring));
            }
        }


        cJSON *jdid= cJSON_GetObjectItem(root,"did");
         if(jdid==NULL){
            sdk_printf("%s: did key not found.\r\n",__FUNCTION__);
            continue;
        }
        if(strcmp((const char *)sdk_context.devid,jdid->valuestring)){
            sdk_printf("%s: device is not match.\r\n",__FUNCTION__);
            continue;
        }

        if(strstr((const char *)data_buffer,"ai.argrace.gw.data.report")){
            //{"call":"ai.argrace.gw.data.report","did":"0806d2079f8d4d52a7c5b98a858e66d8","gwid":0,"ad":"016D","op":"D3","vma":"0710","tid":"A2","data":"1101000000"}
            cJSON *jop = cJSON_GetObjectItem(root,"op");
            cJSON *jvma = cJSON_GetObjectItem(root,"vma");
            cJSON *jtid = cJSON_GetObjectItem(root,"tid");
            cJSON *jdata = cJSON_GetObjectItem(root,"data");
            cJSON *jgwid = cJSON_GetObjectItem(root,"gwid");
            cJSON *jmt = cJSON_GetObjectItem(root,"mt");
            cJSON *jhouseId = cJSON_GetObjectItem(root,"houseId");
            cJSON *jroomId = cJSON_GetObjectItem(root,"roomId");
            if(jop==NULL || jvma==NULL || jtid==NULL || jdata==NULL || jdid==NULL || jgwid==NULL || jmt==NULL || jhouseId ==NULL || jroomId==NULL){
                sdk_printf("%s: key not found.\r\n",__FUNCTION__);
                continue;
            }
            
            if(strcmp(jop->valuestring,"D3")!=0){
                sdk_printf("%s: op=D3,skip.\r\n",__FUNCTION__);
                continue;
            }
            memset(data_cache,0,sizeof(data_cache));
            
            enum_proto_device_type_t need_cb=0;
            int mt = atoi(jmt->valuestring);
            switch(mt){
                case 1://智能插座
                if(strcmp(jvma->valuestring,"0710")==0){ //air condition status
                    //{"call":"ai.argrace.gw.data.report","mt":"0","houseId":"25","roomId":"4829","did":"0806d2079f8d4d52a7c5b98a858e66d8","gwid":0,"ad":"0172","op":"D3","vma":"0710","tid":"88","data":"41F0000000"}
                    //{"call":"ai.argrace.gw.data.report","mt":"0","houseId":"25","roomId":"4829","did":"0806d2079f8d4d52a7c5b98a858e66d8","gwid":0,"ad":"0172","op":"D3","vma":"0710","tid":"8D","data":"F8000000C7"}
                    sdk_utils_string2hexArray(data_cache,5,jdata->valuestring);  
                    if(0==(data_cache[0]&0x80)){
                        memset((uint8_t*)&data,0,sizeof(data));
                        data.outlet.swonoff = (data_cache[0]>>6)&0x01;
                        data.outlet.aircond_onoff = (data_cache[0]>>5)&0x01;
                        data.outlet.temp = data_cache[0]&0x0F;
                        data.outlet.consumption = data_cache[1] |((data_cache[2]<<8)&0xFF00) | ((data_cache[3]&0x3F)<<16);
                        data.outlet.wind_direction_updown = (data_cache[3]>>6)&0x01;
                        data.outlet.wind_direction_leftright = (data_cache[3]>>7)&0x01;
                    }else if(data_cache[0]&0x80){
                        data.outlet.currenct = (data_cache[0]&0x7F) | data_cache[1];
                        data.outlet.power = data_cache[2] |(data_cache[3]<<8);
                        data.outlet.voltage = data_cache[4];
                        need_cb = enum_proto_mt_outlet;
                    }
                }else if(strcmp(jvma->valuestring,"0020")==0){
                 //{"call":"ai.argrace.gw.data.report","mt":"1","houseId":"25","roomId":"4829","did":"0806d2079f8d4d52a7c5b98a858e66d8","gwid":0,"ad":"0172","op":"D3","vma":"0020","tid":"99","data":"01"}
                    memset((uint8_t*)&data,0xFF,sizeof(data));
                    data.outlet.swonoff = (data_cache[0]>>6)&0x01;
                    need_cb = enum_proto_mt_outlet;
                }

                break;

                case 2://电工开关
                case 17://通断器
                //case 35://环境传感器
                //{"call":"ai.argrace.gw.data.report","mt":"2","houseId":"25","roomId":"4829","did":"0806d2079f8d4d52a7c5b98a858e66d8","gwid":0,"ad":"016D","op":"D3","vma":"0710","tid":"B6","data":"1100000000"}
                memset((uint8_t*)&data,0,sizeof(data));
                sdk_utils_string2hexArray(data_cache,5,jdata->valuestring);
                data.sw.switchidx[0]= data_cache[0]&0x0F;
                data.sw.switchidx[1]= (data_cache[0]>>4)&0x0F;
                data.sw.switchidx[2]= data_cache[1]&0x0F;
                data.sw.switchidx[3]= (data_cache[1]>>4)&0x0F;
                need_cb = enum_proto_mt_sw;
                break;

                case 5://灯泡
                case 6://灯带
                case 25://吸顶灯
                case 26://取暖灯
                case 28://二路筒灯
                case 29://冷暖射灯
                case 37://磁吸灯
                //case 55:
                //{"call":"ai.argrace.gw.data.report","mt":"6","houseId":"25","roomId":"4829","did":"0806d2079f8d4d52a7c5b98a858e66d8","gwid":0,"ad":"0168","op":"D3","vma":"0710","tid":"B7","data":"8064006400"}
                //{"call":"ai.argrace.gw.data.report","mt":"6","houseId":"25","roomId":"4829","did":"0806d2079f8d4d52a7c5b98a858e66d8","gwid":0,"ad":"0168","op":"D3","vma":"0710","tid":"B7","data":"8165003F64"}
                memset((uint8_t*)&data,0,sizeof(data));
                sdk_utils_string2hexArray(data_cache,5,jdata->valuestring);
                if( (data_cache[0] & 0x7F)==0){
                    data.cw.onoff =  (data_cache[0] & 0x80) !=0 ? 1:0;
                    data.cw.lnss = data_cache[1];
                    data.cw.colortemp = data_cache[3];
                    need_cb = enum_proto_mt_cw;
                }else if( (data_cache[0] & 0x7F)==1){
                    data.color.onoff =  (data_cache[0] & 0x80) !=0 ? 1:0;
                    data.color.hue = data_cache[1] | (data_cache[2]<<8);
                    data.color.saturation = data_cache[3];
                    data.color.brightness = data_cache[4];
                    need_cb = enum_proto_mt_hsb;
                }else{
                    sdk_printf("%s:unsupport light type[%02x],mt[%d].\r\n",__FUNCTION__,(data_cache[0] & 0x7F),jmt->valueint);
                }
                break;

                case 33://中央空调网关(生产环境)
                //case 44://中央空调网关(测试环境)
                //{"call":"ai.argrace.gw.data.report","mt":"44","houseId":"25","roomId":"4829","did":"0806d2079f8d4d52a7c5b98a858e66d8","gwid":0,"ad":"0166","op":"D3","vma":"0710","tid":"86","data":"000020810F"}
                memset((uint8_t*)&data,0,sizeof(data));
                sdk_utils_string2hexArray(data_cache,5,jdata->valuestring);
                data.aircond.onoff = data_cache[0]&0x01;
                data.aircond.tempInRoom = (data_cache[0]>>1)&0x0F;
                data.aircond.windspeed = (data_cache[0]>>5)&0x7;
                data.aircond.mode = data_cache[1]&0x0F;
                data.aircond.error = (data_cache[1]>>4)&0x07;
                data.aircond.onoffline = (data_cache[1]>>7)&0x01;
                data.aircond.temp = data_cache[2]&0x0F;
                data.aircond.antifreeze_winddirection = (data_cache[2]>>4)&0x0F;
                need_cb =  enum_proto_mt_aircond;
                break;

                case 34://窗帘(生产环境)
                //case 49://窗帘(测试环境)
                //{"call":"ai.argrace.gw.data.report","mt":"49","houseId":"25","roomId":"4829","did":"0806d2079f8d4d52a7c5b98a858e66d8","gwid":0,"ad":"0164","op":"D3","vma":"0710","tid":"A0","data":"4064"}
                memset((uint8_t*)&data,0,sizeof(data));
                if(strlen(jdata->valuestring)==4){ 
                    sdk_utils_string2hexArray(data_cache,2,jdata->valuestring);
                    data.curtain.motorOnOff = (data_cache[0] >> 6) &0x03;
                    data.curtain.direction =  (data_cache[0] >> 4) &0x03;
                    data.curtain.handdrag = (data_cache[0] >> 3) &0x01;
                    data.curtain.motorPosition = data_cache[1];
                    need_cb = enum_proto_mt_curtain;
                }else if(strlen(jdata->valuestring)==8){
                    sdk_utils_string2hexArray(data_cache,4,jdata->valuestring);
                    data.curtain.motorOnOff = (data_cache[0] >> 6) &0x03;
                    data.curtain.direction =  (data_cache[0] >> 4) &0x03;
                    data.curtain.handdrag = (data_cache[0] >> 3) &0x01;
                    data.curtain.motorPosition = data_cache[1];
                    data.curtain.angleDirection = data_cache[2];
                    data.curtain.andglePercentage = data_cache[3];
                    need_cb = enum_proto_mt_curtain;
                }

                break;

                case 8://门磁
                case 9://人体传感器
                //nothing
                break;
                case 30://随意贴-无线开关
                //nothing
                break;
                case 46://空气净化灯(生产环境)
                case 65://空气净化灯(测试环境)
                //nothing
                break;
                case 48://温控器
                //nothing
                break;
         
                default:
                //nothing
                break;

            }
            
           if(need_cb && device_data_cb){
                device_data_cb(addr,need_cb,&data);
           }


        }else if(strstr((const char *)data_buffer,"ai.argrace.gw.online")){
            //{"call":"ai.argrace.gw.online","did":"0806d2079f8d4d52a7c5b98a858e66d8","type":"device","ad":"0144","extdata":""}
            if(device_onoffline_cb){
                device_onoffline_cb(addr,1);
            }
        }else if(strstr((const char *)data_buffer,"ai.argrace.gw.offline")){
            if(device_onoffline_cb){
                device_onoffline_cb(addr,0);
            }
        }


        cJSON_Delete(root);
    }
    return (void*)NULL;
}

void *sdk_proto_auth_recv_thread(void *arg)
{
    char targetip_rec[16]={0};
    uint16_t target_data_port=0;
    while(1){
        memset(auth_buffer,0,auth_buffer_len);
        int ret = sdk_proto_auth_recv(auth_buffer,auth_buffer_len);
        if(ret>0){

            if(strstr((const char *)auth_buffer,"ai.argrace.gw.auth")){
                //setup data receive thread
                if(sdk_socket_data_fd!=-1){
                    //check ip and port
                    if( (target_data_port == sdk_context.port) && (strcmp(sdk_context.targetIp,targetip_rec)==0) ){
                        sdk_printf("%s:data socket not need to change.\r\n",__FUNCTION__);
                        continue;
                    }
                    sdk_port_udp_socket_close(&sdk_socket_data_fd);
                }

                //context record
                cJSON *root = cJSON_Parse((const char *)auth_buffer);
                if(!root){
                    sdk_printf("%s: auth json parse error.\r\n",__FUNCTION__);
                    continue;
                }

                cJSON *jak = cJSON_GetObjectItem(root,"ak");
                cJSON *jdid = cJSON_GetObjectItem(root,"id");
                cJSON *jcode = cJSON_GetObjectItem(root,"code");
                cJSON *jgwid = cJSON_GetObjectItem(root,"gwid");
                cJSON *jport = cJSON_GetObjectItem(root,"port");
                cJSON *jmac = cJSON_GetObjectItem(root,"mac");
                cJSON *jip = cJSON_GetObjectItem(root,"ip");
                if(jak==NULL || jdid==NULL || jcode==NULL || jport==NULL || jgwid==NULL || jip==NULL ||jmac==NULL){
                    sdk_printf("%s: auth json key not found.\r\n",__FUNCTION__);
                    continue;
                }    

                memset(sdk_context.accesskey,0,sizeof(sdk_context.accesskey));
                strcpy(sdk_context.accesskey,jak->valuestring);
                memset(sdk_context.authOrig,0,sizeof(sdk_context.authOrig));
                strcpy(sdk_context.authOrig,(const char *)auth_buffer);
                memset(sdk_context.mac,0,sizeof(sdk_context.mac));
                strcpy(sdk_context.mac,jmac->valuestring);
                memset(sdk_context.ip,0,sizeof(sdk_context.ip));
                strcpy(sdk_context.ip,jip->valuestring);
                sdk_context.port = atoi(jport->valuestring) &0xFFFF;
                sdk_context.gwid = atoi(jgwid->valuestring) &0xFFFF;
                cJSON_Delete(root);

                target_data_port = sdk_context.port;
                memset(targetip_rec,0,sizeof(targetip_rec));
                strcpy(targetip_rec,sdk_context.targetIp);
                if(0!=sdk_port_udp_socket_create(&sdk_socket_data_fd,sdk_context.port)){
                    sdk_printf("%s:data socket create error.\r\n",__FUNCTION__);
                    continue;
                }

                //check thread 
                if(data_thread!=-1){
                    sdk_port_thread_delete(&data_thread);
                }

                if(sdk_port_thread_create(&data_thread,"data thread",sdk_proto_data_recv_thread,NULL,10*1024,5)!=0){
                    sdk_printf("%s:thread create error.\r\n",__FUNCTION__);
                    continue;
                }

            }else if(strstr((const char *)auth_buffer,"ai.argrace.gw.unregist")){
                sdk_proto_context_unregist_clear();
                sdk_printf("%s:context clear auth data.\r\n",__FUNCTION__);
            }else if(strstr((const char *)auth_buffer,"ai.argrace.gw.hello")){
                cJSON * root = cJSON_Parse((const char *)auth_buffer);
                if(root){
                    cJSON * jcode = cJSON_GetObjectItem(root,"code");
                    cJSON * jdid = cJSON_GetObjectItem(root,"did");
                    cJSON * jak = cJSON_GetObjectItem(root,"ak");
                    cJSON * jtype = cJSON_GetObjectItem(root,"type");
                    cJSON * jad = cJSON_GetObjectItem(root,"ad");
                    cJSON * jextdata = cJSON_GetObjectItem(root,"extdata");
                    if(jcode ==NULL || jdid ==NULL || jak==NULL || jtype==NULL || jad==NULL || jextdata==NULL){
                        sdk_printf("%s:heartbeat key not found.\r\n",__FUNCTION__);
                    }else{
                        //check
                        if(strcmp(jtype->valuestring,"gw")==0){
                            if(func_ptr){
                                func_ptr(jcode->valueint);
                                func_ptr = NULL;
                            }
                            
                        }else{
                            sdk_printf("%s:heartbeat type must be 'gw'.\r\n",__FUNCTION__);
                        }
                    }
                    cJSON_Delete(root);                    
                }else{
                    sdk_printf("%s:smartgw hello ack json parse error.\r\n",__FUNCTION__);   
                }

                sdk_printf("%s:smartgw hello ack.\r\n",__FUNCTION__);    
            }else{
                sdk_printf("%s:unsupport method.\r\n",__FUNCTION__);
            }
        }
    }
    return (void*)NULL;
}


int sdk_proto_auth_recv_thread_init(void)
{
    if(auth_buffer==NULL || auth_buffer_len < 0){
        sdk_printf("%s:buffer error.\r\n",__FUNCTION__);
        return SDK_BUFFER_ERR;
    }

    //check thread 
    if(auth_thread!=-1){
        sdk_port_thread_delete(&auth_thread);
    }

    if(sdk_port_thread_create(&auth_thread,"auth thread",sdk_proto_auth_recv_thread,NULL,10*1024,5)!=0){
        sdk_printf("%s:thread create error.\r\n",__FUNCTION__);
        return SDK_THREAD_ERR;
    }

    return SDK_OK;
}




